﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Reflection;
using System.CodeDom.Compiler;
using Microsoft.CSharp;
using GeneratorLib.DB;
using GeneratorLib.DB.Connectios;
using GeneratorLib.DB.Schemas;

namespace Generator.TemplateGenerators
{
    #region Compiler

    public class Context
    {
        public  CSharpCodeProvider CodeProvider { get; set; }
        public IDictionary<String, Assembly> Assemblys { get; set; }

        public Context()
        {
            CodeProvider = new CSharpCodeProvider(new Dictionary<string, string>() { { "CompilerVersion", "v4.0" } });
            Assemblys = new Dictionary<String, Assembly>();
            Assembly[] al = AppDomain.CurrentDomain.GetAssemblies();
            foreach (Assembly a in al)
            {
                AddAssembly(a);
            }
            AppDomain.CurrentDomain.AssemblyLoad += new AssemblyLoadEventHandler(CurrentDomain_AssemblyLoad);
        }

        private void AddAssembly(Assembly a)
        {
            if (a != null)
            {
                Assemblys.Add(a.FullName, a);
            }
        }

        void CurrentDomain_AssemblyLoad(object sender, AssemblyLoadEventArgs args)
        {
            Assembly a = args.LoadedAssembly;
            if (!Assemblys.ContainsKey(a.FullName))
            {
                AddAssembly(a);
            }
        }

        public CompilerParameters CreateCompilerParameters()
        {
            CompilerParameters cp = new CompilerParameters();
            cp.GenerateExecutable = false;
            cp.GenerateInMemory = true;
            if (Assemblys != null)
            {
                foreach (Assembly a in Assemblys.Values)
                {
                    if (!cp.ReferencedAssemblies.Contains(a.Location))
                        cp.ReferencedAssemblies.Add(a.Location);
                }
            }

            string dllPath = System.IO.Path.Combine(GlobalValues.CurrentPath, "GeneratorCode.dll");
            if (!cp.ReferencedAssemblies.Contains(dllPath))
                cp.ReferencedAssemblies.Add(dllPath);

            dllPath = System.IO.Path.Combine(GlobalValues.CurrentPath, "GeneratorLib.dll");
            if (!cp.ReferencedAssemblies.Contains(dllPath))
                cp.ReferencedAssemblies.Add(dllPath);

            dllPath = System.IO.Path.Combine(GlobalValues.CurrentPath, "ICSharpCode.TextEditor.dll");
            if (!cp.ReferencedAssemblies.Contains(dllPath))
                cp.ReferencedAssemblies.Add(dllPath);


            return cp;
        }

        public string ToCsharpCode(TableInfo table, String sourceCode)
        { 
            String inputCmdString = sourceCode.Trim();
            if (String.IsNullOrEmpty(inputCmdString)) return "";

            String fullCmd = BuildFullCmd(inputCmdString);

            return Invoke(table, fullCmd, "ToCsharpCode", null);
        }

        public string GetTemplateConfig(TableInfo table, String sourceCode)
        { 
            String inputCmdString = sourceCode.Trim();
            if (String.IsNullOrEmpty(inputCmdString)) return "";

            String fullCmd = BuildFullCmd2(sourceCode);
            return Invoke(table, fullCmd, "GetTemplateConfig", null);
        }

        private string Invoke(TableInfo table, String fullCmd, string MethodInstance,object[] MethodParam)
        {

            CompilerResults cr = CodeProvider.CompileAssemblyFromSource(CreateCompilerParameters(), fullCmd);

            if (cr.Errors.HasErrors)
            {
                Boolean recompileSwitch = true;

                foreach (CompilerError err in cr.Errors)
                {
                    //CS0201 : Only assignment, call, increment, decrement, and new object expressions can be
                    //used as a statement
                    if (!err.ErrorNumber.Equals("CS0201"))
                    {
                        recompileSwitch = false;
                        break;
                    }
                }

                //// recompile
                if (recompileSwitch)
                {
                    cr = CodeProvider.CompileAssemblyFromSource(CreateCompilerParameters(), fullCmd);
                }

                if (cr.Errors.HasErrors)
                {
                    StringBuilder sb = new StringBuilder();
                    sb.AppendLine("Compiler Error：");
                    foreach (CompilerError err in cr.Errors)
                    {
                        sb.AppendLine(err.ErrorNumber);
                        sb.AppendLine(err.ErrorText);
                    }
                    return sb.ToString();
                }
            }

            Assembly assem = cr.CompiledAssembly;
            Object dynamicObject = assem.CreateInstance("Generator.DynamicClass");
            Type t = assem.GetType("Generator.DynamicClass");

            MethodInfo SetTableInfo = t.GetMethod("SetTableInfo");
            SetTableInfo.Invoke(dynamicObject, new object[] { table });

            MethodInfo minfo = t.GetMethod(MethodInstance);
            return (string)minfo.Invoke(dynamicObject, MethodParam);
        }

        private String BuildFullCmd(String inputCmdString)
        {
            String fullCmd = String.Empty;

            fullCmd += @"

                using System;
                using System.Collections.Generic;
                using System.ComponentModel;
                using System.Data;
                using System.Drawing;
                using System.Linq;
                using System.Text;
                using System.Windows.Forms;
                using GeneratorLib;
                using GeneratorLib.DB;
                using GeneratorLib.DB.Connectios;
                using GeneratorLib.DB.Schemas;

                namespace Generator 
                { 
                    public class DynamicClass
                    {

                        public TableHost TableHost{get;private set;}                        
                        public void SetTableInfo(TableInfo table)
                        {
                            this.TableHost=new TableHost();
                            this.TableHost.SetTableInfo(table);
                        }

                        public string ToCsharpCode()
                        {
                            CodeBuilder codeBuilder = new CodeBuilder();
                            " + inputCmdString + @"
                            return codeBuilder.ToString();
                        }

                        public string GetTemplateConfig(string inParam)
                        {
                            return inParam;
                        }

                        private string serialStr(string str)
                        {
                            return Convert.ToBase64String(System.Text.Encoding.UTF8.GetBytes(str));
                        }

                        private string deSerialStr(string str)
                        {
                            return System.Text.Encoding.UTF8.GetString(Convert.FromBase64String(str));
                        }

                    }
                }";
            return fullCmd;
        }

        private String BuildFullCmd2(String inputCmdString)
        {
            String fullCmd = String.Empty;

            fullCmd += @"

                using System;
                using System.Collections.Generic;
                using System.ComponentModel;
                using System.Data;
                using System.Drawing;
                using System.Linq;
                using System.Text;
                using System.Windows.Forms;
                using GeneratorLib;
                using GeneratorLib.DB;
                using GeneratorLib.DB.Connectios;
                using GeneratorLib.DB.Schemas;

                namespace Generator 
                { 
                    public class DynamicClass
                    {

                        public TableHost TableHost{get;private set;}                        
                        public void SetTableInfo(TableInfo table)
                        {
                            this.TableHost=new TableHost();
                            this.TableHost.SetTableInfo(table);
                        }

                        public string GetTemplateConfig()
                        {
                            return " + inputCmdString + @"
                        }

                    }
                }";
            return fullCmd;
        }

    }


    #endregion
}
